﻿/*
ParallelPlatform Project - A web platform to distribute and share knowledge
Online Editor - An online tool to create and play animations
Copyright (C) 2011 Daniel Guerrero Martínez & Sergio Rodríguez Lumley

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package Classes{
	public class BasicFileModule{
		
		// Map of replacing matches, pair positions are the keys,
		// impair positions the replacemens
		// ***** READ BEFORE ADDING OR EDITING KEYS!!! ******
		// The only restriction to keys is that non can be a part of other,
		// for example, "<type>" cannot be a key itself, because
		// it would nullify "<type>change_depth</type>", among many others...
		static private var KEY_MAP:Array = 
				["<?xml version=\"1.0\" encoding=\"UTF-8\"?>", "<xtg>",
				"<action>", "<a>",
				"</action>", "</a>",
				"<actions>", "<a1>",
				"</actions>", "</a1>",
				"<animation>", "<a2>",
				"</animation>", "</a2>",
				"<body>", "<b>",
				"</body>", "</b>",
				"<blurx>", "<b1>",
				"</blurx>", "</b1>",
				"<blury>", "<b2>",
				"</blury>", "</b2>",
				"<classtype>", "<c>",
				"</classtype>", "</c>",
				"<contributor>", "<c1>",
				"</contributor>", "</c1>",
				"<color>", "<c2>",
				"</color>", "</c2>",
				"<coloredlines>", "<c3>",
				"</coloredlines>", "</c3>",
				"<delay>", "<d>",
				"</delay>", "</d>",
				"<depth>", "<d1>",
				"</depth>", "</d1>",
				"<description>", "<d2>",
				"</description>", "</d2>",
				"<header>", "<h>",
				"</header>", "</h>",
				"<id>", "<i>",
				"</id>", "</i>",
				"<line>", "<l>",
				"</line>", "</l>",
				"<num>", "<n>",
				"</num>", "</n>",
				"<paint>", "<p>",
				"</paint>", "</p>",
				"<rotate>", "<r>",
				"</rotate>", "</r>",
				"<scalex>", "<s>",
				"</scalex>", "</s>",
				"<scaley>", "<s1>",
				"</scaley>", "</s1>",
				"<scene>", "<s2>",
				"</scene>", "</s2>",
				"<text>", "<t>",
				"</text>", "</t>",
				"<title>", "<t1>",
				"</title>", "</t1>",
				"<type>change_depth</type>", "<t2>",
				"<type>change_text</type>", "<t3>",
				"<type>insert</type>", "<t4>",
				"<type>instant</type>", "<t5>",
				"<type>move</type>", "<t6>",
				"<type>normal</type>", "<t7>",
				"<type>reader</type>", "<t8>",
				"<type>remove</type>", "<t9>",
				"<type>rotate</type>", "<ta>",
				"<type>scale</type>", "<tb>",
				"<type>blur</type>", "<tc>",
				"<type>paint</type>", "<te>",
				"<version>", "<v>",
				"</version>", "</v>"
				//"<x>", "<x>",
				//"<y>", "<y>"
				];
		
		/**
		  Encrypts the given file
		*/
		static public function encrypt(str:String):String{
			//trace("FileModule: encrypt() -> Not yet implemented!");
			// Encryption support, add here an algorithm to encrypt.
			// Keep in mind that a mark to indicate that the string
			// has been encrypted should be added, as one should be
			// able to save files without encryption
			return str;
		}
		
		/**
		  Decrypts the given file
		*/
		static public function decrypt(str:String):String{
			//trace("FileModule: decrypt() -> Not yet implemented!");
			// Decryption support, add here an algorithm to decrypt.
			// Keep in mind this algorithm must support uncrypted files,
			// so when a file is encrypted it should decrypt it,
			// otherwise, just return the same string
			return str;
		}
		
		/**
		  Compacts the given file according to editor keywords
		*/
		static public function compact(str:String):String{
			var nstr:String = "";
			var word:String = "";
			var match:int = -2;
			var i:uint = 0;
			
			for(i=0; i<str.length; ++i){
				word += str.charAt(i);
				match = BasicFileModule.keyMatch(word);
				if(match <= -2){
					nstr += word;
					word = "";
				}else if(match >= 0){
					nstr += KEY_MAP[match+1];
					word = "";
				}
			}
			nstr += word;
			
			return nstr;
		}
		
		/**
		  Decompacts the given file according to editor keywords
		*/
		static public function decompact(str:String):String{
			var nstr:String = "";
			var word:String = "";
			var match:int = -2;
			var i:uint = 0;
			
			for(i=0; i<str.length; ++i){
				word += str.charAt(i);
				match = BasicFileModule.reverseKeyMatch(word);
				if(match <= -2){
					nstr += word;
					word = "";
				}else if(match >= 0){
					nstr += KEY_MAP[match-1];
					word = "";
				}
			}
			nstr += word;
			
			return nstr;
		}
		
		/**
		  Returns the given file in XML form
		*/
		static public function stringToXML(str:String):XML{
			return new XML(str);
		}
		
		/**
		  Returns the given XML in a String form
		*/
		static public function xmlToString(xmlFile:XML):String{
			return xmlFile.toString();
		}
		
		
		static public function getTitle(xmlFile:XML):String{
			return xmlFile.header.title.text();
		}
		
		static public function getDescription(xmlFile:XML):String{
			return xmlFile.header.description.text();
		}
		
		static public function getDelay(xmlFile:XML):Number{
			return Number(xmlFile.header.delay.text());
		}
		
		static public function getSceneList(xmlFile:XML):XMLList{
			return xmlFile.body.scene;
		}
		
		static public function getSceneActions(sceneElement:XML):XMLList{
			return sceneElement.actions.action;
		}
		
		static public function getSceneType(sceneElement:XML):String{
			return sceneElement.type.text();
		}
		
		static public function getAuthors(xmlFile:XML):Array{
			var array:Array = new Array();
			for each(var contributor in xmlFile.header.contributor){
				array.push(contributor.text());
				//trace(contributor.text());
			}
			return array;
		}
		
		// ******** PRIVATE FUNCTIONS ********** //
		/**
		  Tries to match the given string with all the keys returning
		  if there is no match, partial match or full match
		  @param str String to match with the keys
		  @return uint with the following code:
		  	[-n..-2] 	- No match
				-1 		- Partial match with a key word
			[0...n] 	- Full match with the index of the key-match
		*/
		static private function keyMatch(str:String):int{
			var match:int = -2; // No match by default
			var i:int = 0;
			
			// Uncomment these lines if cheat is removed!!!
			for(i=0; i<KEY_MAP.length && match == -2; i+=2){
				 //We search the string in the KEY
				if(String(KEY_MAP[i]).indexOf(str) == 0){
					match = -1;
				}
			}
			
			if(match > -2){
				// We continue from the first match we had
				for(i = Math.max(0, i-2); i<KEY_MAP.length && match < 0; i+=2){
					// We search the KEY in the string, in this case, the match is complete
					if(str.indexOf(KEY_MAP[i]) == 0){
						match = i;
					}
				}
			}
			
			return match;
		}
		
		/**
		  Tries to match the given string with all the replacements
		  for the keys returning if there is no match, partial match
		  or full match 
		  @param str String to match with the replacements
		  @return uint with the following code:
		  	[-n..-2] 	- No match
				-1 		- Partial match
			[0...n] 	- Full match with the index of the key-replacement
		*/
		static private function reverseKeyMatch(str:String):int{
			var match:int = -2; // No match by default
			var i:int = 0;
			
			// Uncomment these lines if cheat is removed!!!
			for(i=1; i<KEY_MAP.length && match == -2; i+=2){
				 //We search the string in the KEY
				if(String(KEY_MAP[i]).indexOf(str) == 0){
					match = -1;
				}
			}
			
			if(match > -2){
				// We continue from the first match we had
				for(i = Math.max(1, i-2); i<KEY_MAP.length && match < 0; i+=2){
					// We search the KEY in the string, in this case, the match is complete
					if(str.indexOf(KEY_MAP[i]) == 0){
						match = i;
					}
				}
			}
			
			return match;
		}
		
		
	}
}
